#include "sfc_famicom.h"
#include "log.h"

#include <assert.h>
#include <strings.h>
#include <stdio.h>
#include <stdlib.h>

// static 函数，仅在该文件中可见
static sfc_ecode sfc_load_default_rom(void*, sfc_rom_info_t*);
static sfc_ecode sfc_free_default_rom(void*, sfc_rom_info_t*);
static sfc_ecode sfc_load_new_rom(sfc_famicom_t* famicom);

extern sfc_ecode sfc_load_mapper(sfc_famicom_t* famicom, uint8_t id);

typedef void(*sfc_funcptr_t)();

sfc_ecode sfc_famicom_init(sfc_famicom_t* famicom, void* argument, const sfc_interface_t* interfaces) {
	assert(famicom && "bad famicom");

	famicom->argument = argument;
	famicom->interfaces.load_rom = sfc_load_default_rom;
	famicom->interfaces.free_rom = sfc_free_default_rom;
	memset(&famicom->rom_info, 0, sizeof(famicom->rom_info));

	// 把 CPU RAM 和 SRAM 挂载到 prg_banks
	famicom->prg_banks[0] = famicom->main_memory;
	famicom->prg_banks[3] = famicom->save_memory;
	
	if (interfaces) {
		//TODO: 实现 interfaces 解析
		assert(interfaces && "bad interface");
	}

	// return famicom->interfaces.load_rom(argument, &famicom->rom_info);
	return sfc_load_new_rom(famicom);
}


void sfc_famicom_uninit(sfc_famicom_t* famicom) {
	famicom->interfaces.free_rom(famicom->argument, &famicom->rom_info);
}

sfc_ecode sfc_load_default_rom(void* arg, sfc_rom_info_t* info) {
	assert(info->data_prgrom == NULL && "FREE FIRST");
	const char* default_rom = "rom/nes_test_rom.nes";
	FILE* file = fopen(default_rom, "rb");
	if (!file) {
		LOG("err: rom not found\n");
		return SFC_ERROR_FILE_NOT_FOUND;
	}
		
	
	sfc_ecode code = SFC_ERROR_ILLGAL_FILE;

	// 载入 rom 头
	sfc_nes_header_t nes_header;
	if  (!fread(&nes_header, sizeof(nes_header), 1, file)) {
		code = SFC_ERROR_ILLGAL_FILE;
		LOG("err: rom is a empty file\n");
		goto CLOSEFILE;
	}

	// 解析 rom 头
	if (nes_header.id != NES_MAGIC_NUM) {
		code = SFC_ERROR_ILLGAL_FILE;
		LOG("err: rom magic num error %x, should is %x\n", nes_header.id, NES_MAGIC_NUM);
		goto CLOSEFILE;
	}
	const size_t prgrom_size = 16 * 1024 * nes_header.count_prgrom_16kb;
	const size_t chrrom_size = 8 * 1024 * nes_header.count_chrrom_8kb;
	uint8_t* const ptr = (uint8_t*)malloc(prgrom_size + chrrom_size);
	if (!ptr) {
		code = SFC_ERROR_OUT_OF_MEMORY;
		LOG("err: alloc ptr fail\n");
		goto CLOSEFILE;
	}

	{
		//TODO: 实现 trainer， 目前先跳过
		if (nes_header.control1 & SFC_NES_TRAINER)
			fseek(file, 512, SEEK_CUR);
	}

	fread(ptr, prgrom_size + chrrom_size, 1, file);
	info->data_prgrom = ptr;
	info->data_chrrom = ptr + prgrom_size;
	info->count_prgrom_16kb = nes_header.count_prgrom_16kb;
	info->count_chrrom_8kb = nes_header.count_chrrom_8kb;
	// control1 的高四位做 num 的低四位， control2 的高四位做 num 的高四位
	info->mapper_number = (nes_header.control1 >> 4) | (nes_header.control2 & 0XF0);
	info->vmirroring = (nes_header.control1 & SFC_NES_VMIRROR);
	info->four_screen = (nes_header.control1 & SFC_NES_4SCREEN);
	info->save_ram = (nes_header.control1 & SFC_NES_SAVEROM);
	
	assert(!(nes_header.control1 & SFC_NES_TRAINER) && "unsupported");
	assert(!(nes_header.control2 & SFC_NES_VS_UNISYSTEM) && "unsupported");
	assert(!(nes_header.control2 & SFC_NES_Playchoice10) && "unsupported");

	code = SFC_ERROR_OK;
	LOG("load nes rom %s succ\n", default_rom);

CLOSEFILE:
	fclose(file);
	return code;
}

sfc_ecode sfc_free_default_rom(void* arg, sfc_rom_info_t* info) {
	// 非 NULL 才需要释放
	if (!info->data_prgrom) {
		free(info->data_prgrom);
		info->data_prgrom = NULL;
	} else {
		LOG("try to free a null ptr\n");
	}
	return SFC_ERROR_OK;
}

sfc_ecode sfc_load_new_rom(sfc_famicom_t* famicom) {
	// 释放旧 ROM
	sfc_ecode code = famicom->interfaces.free_rom(famicom->argument, &famicom->rom_info);
	if (code)
		goto RET;

	memset(&famicom->rom_info, 0, sizeof(famicom->rom_info));

	// 载入新 ROM
	code = famicom->interfaces.load_rom(famicom->argument, &famicom->rom_info);
	if (code) 
		goto RET;

	// 载入 mapper
	code = sfc_load_mapper(famicom, famicom->rom_info.mapper_number);
	if (code)
		goto RET;

	// 首次重置 mapper
	code = famicom->mapper.reset(famicom);
	if (code)
		goto RET;

RET:
	return code;
}